#include "seg.h"
listener *listeners = NULL;

void events_init(void){
    command_add(term,"evt",&cmd_event,2,0);
    command_add(term,"event_listen",&cmd_event_listen,1,0);
}

void cmd_event_listen(plugin *p, const char **argv, int argc){
    filter *new_filter;
    if ((argc-1)%3){
        tell(p,"err malformed listen\n");
        return;
    }
    listener *new_listener = malloc(sizeof(listener));
    int i;
    for(i=1;i<argc;i+=3){
        const char *temp_name = argv[i+0];
        const char *temp_operator = argv[i+1];
        const char *temp_value = argv[i+2];
        new_filter = malloc(sizeof(filter));
        new_filter->name = strndup(temp_name,MAX_ATTR_LEN);
        new_filter->value = strndup(temp_value,MAX_ATTR_LEN);
        if (0==strncmp(temp_operator,"==",2)){
            new_filter->cmp = cmp_streq;
        } else if (0==strncmp(temp_operator,">=",2)){
            new_filter->cmp = cmp_ge;
        } else if (0==strncmp(temp_operator,"<=",2)){
            new_filter->cmp = cmp_le;
        } else if (0==strncmp(temp_operator,">",1)){
            new_filter->cmp = cmp_ge;
        } else if (0==strncmp(temp_operator,"<",1)){
            new_filter->cmp = cmp_le;
        } else if (0==strncmp(temp_operator,"~=",4)){
            new_filter->cmp = cmp_streq;
        } else if (0==strncmp(temp_operator,"STARTSWITH",10)){
            new_filter->cmp = cmp_streq;
        } else if (0==strncmp(temp_operator,"ENDSWITH",8)){
            new_filter->cmp = cmp_streq;
        } else if (0==strncmp(temp_operator,"=",1)){
            new_filter->cmp = cmp_eq;
        } else {
            tell(p,"err format\n");
            free(new_filter);
            free(new_listener);
            /*memory leak*/
            return;
        }
        new_filter->next = NULL;
        if (new_listener->head == NULL){
            new_listener->head = new_filter;
        } else {
            filter *temp_filter;
            temp_filter = new_listener->head;
            while(temp_filter && temp_filter->next){
                temp_filter = temp_filter->next;
            }
            temp_filter->next = new_filter;
        }
        new_listener->tail = new_filter;
    }
    new_listener->origin = p;
    listener_add(new_listener);
}

void listener_add(listener *_listener){
    listener *temp_listener = listeners;
    while(temp_listener && temp_listener->next){
        temp_listener = temp_listener->next;
    }
    if (temp_listener == NULL){
        listeners = _listener;
    } else {
        temp_listener->next = _listener;
    }
}

void cmd_event(plugin *p, const char **argv, int argc){
    field *fields[(argc-1)/2];
    int i,j;
    event *evt;
    if (argc < 2 || 0 == (argc%2)){
        tell(p,"err malformed event\n");
        return;
    }

    evt = malloc(sizeof(event));
    for(i=1,j=0;i<argc;i+=2,j++){
        field *temp_field = malloc(sizeof(field));
        temp_field->name = strndup(argv[i],MAX_ATTR_LEN);
        temp_field->value = strndup(argv[i+1],MAX_ATTR_LEN);
        fields[j] = temp_field;
    }
    evt->source = p;
    evt->fields = fields;
    evt->num_fields = j;
    exec_event(evt);
}

void exec_event(event *evt){
    filter *temp_filter;
    listener *temp_listener;
    for(temp_listener = listeners;temp_listener;temp_listener = temp_listener->next){
        printf("listener\n");
        filter_match temp_match=FAIL;
        for(temp_filter = temp_listener->head;temp_filter;temp_filter = temp_filter->next){
            printf("filter: %s %s\n",temp_filter->name,temp_filter->value);
            temp_match = match(temp_filter,evt);
            if (temp_match == MATCH){
                printf("filter match!\n");
            } else if (temp_match == FAIL){
                printf("filter fail\n");
                break;
            } else if (temp_match == DROP){
                printf("filter drop\n");
                return;
            }
        }
        if (temp_match != FAIL){
            printf("we've got a winner: %d\n",temp_match);
            tell(temp_listener->origin,"%s",evt2str(evt));
        }
    }
}

filter_match match(filter *_filter, event *_event){
    field **temp_field;
    int i;
    for(i=0,temp_field=_event->fields;i<_event->num_fields;i++){
        if (0==strncmp(_filter->name,temp_field[i]->name,MAX_ATTR_LEN)){
            int retval = _filter->cmp(_filter->value,temp_field[i]->value);
            if (retval == 0){
                return MATCH;
            }
            if (retval > 0){
                return FAIL;
            }
            return FAIL;
        }
        DEBUG("no\n");
    }
    DEBUG("filter didn't match any fields\n");
    return FAIL;
}

char *evt2str(event *evt){
    char *ret = malloc(MAX_ATTR_LEN*MAX_FIELDS);
    strcpy(ret,"evt");
    char *buf = malloc(MAX_ATTR_LEN);
    field **temp_field;
    int i;
    for(i=0,temp_field=evt->fields;i<evt->num_fields;i++){
        sprintf(buf," %s %s",temp_field[i]->name,temp_field[i]->value);
        strcat(ret,buf);
    }
    strcat(ret,"\n");
    return ret;
}    

int cmp_ge(void *_a, void *_b){
    double a = strtod((const char*)_a,NULL);
    if (a == 0 && errno == ERANGE) return -1;
    double b = strtod((const char*)_b,NULL);
    if (b == 0 && errno == ERANGE) return -1;
    if (a >= b) return 0;
    return 1;
}

int cmp_le(void *_a, void *_b){
    double a = strtod((const char*)_a,NULL);
    if (a == 0 && errno == ERANGE) return -1;
    double b = strtod((const char*)_b,NULL);
    if (b == 0 && errno == ERANGE) return -1;
    if (a <= b) return 0;
    return 1;
}

int cmp_eq(void *_a, void *_b){
    double a = strtod((const char*)_a,NULL);
    if (a == 0 && errno == ERANGE) return -1;
    double b = strtod((const char*)_b,NULL);
    if (b == 0 && errno == ERANGE) return -1;
    if (a == b) return 0;
    return 1;
}

int cmp_streq(void *_a, void *_b){
    int retval = strncmp((const char*)_a,(const char*)_b,strlen(_a));
    if (retval < 0) return -retval;
    return retval;
}

